#include "cem/config_directory.h"
#include "cem/json_config_builder.h"
#include "cem/parameter_reader.h"

#include <algorithm>
#include <cctype>
#include <cjson/cJSON.h>
#include <cstdio>
#include <cstdlib>
#include <fstream>
#include <sstream>
#include <string>
#include <unordered_map>
#include <vector>

namespace cem
{

namespace
{

// 去除字符串首尾空白字符
static inline std::string
trim(const std::string & s)
{
    size_t b = 0, e = s.size();
    while (b < e && std::isspace(static_cast<unsigned char>(s[b])))
        ++b;
    while (e > b && std::isspace(static_cast<unsigned char>(s[e - 1])))
        --e;
    return s.substr(b, e - b);
}

// 大小写无关字符串相等
static inline bool
iequals(const std::string & a, const std::string & b)
{
    if (a.size() != b.size())
        return false;
    for (size_t i = 0; i < a.size(); ++i)
        if (std::tolower((unsigned char)a[i]) != std::tolower((unsigned char)b[i]))
            return false;
    return true;
}

// 诊断路径拼接（供 ConfigDirectory 诊断用）
static inline std::vector<std::string>
append_path(const std::vector<std::string> & base, const std::string & key)
{
    auto p = base;
    p.push_back(key);
    return p;
}

// 利用 ConfigDirectory 解析 PHYSICAL_AREA 下的 {id,value} 数组到映射
static void
parse_id_value_array_from_config(const ConfigDirectory & root,
                                 const std::string & array_key,
                                 std::unordered_map<int, double> & out_map)
{
    out_map.clear();
    auto pa_dir_opt = root.subdir("PHYSICAL_AREA");
    if (!pa_dir_opt)
        return;
    auto arr_opt = pa_dir_opt->get_array(array_key);
    if (!arr_opt)
        return;

    for (const auto & item_ptr : arr_opt->items)
    {
        if (!item_ptr)
            continue;
        if (!std::holds_alternative<std::shared_ptr<ConfigObject>>(item_ptr->value))
            continue;
        auto obj_ptr = std::get<std::shared_ptr<ConfigObject>>(item_ptr->value);
        if (!obj_ptr)
            continue;
        ConfigDirectory item_dir(array_key, *obj_ptr, {});
        auto id64 = item_dir.get_int64("id");
        auto val = item_dir.get_double("value");
        if (id64 && val)
            out_map[static_cast<int>(*id64)] = *val;
    }
}

} // namespace

bool
ParameterReader::parse_cli(int argc, char ** argv)
{
    for (int i = 1; i < argc; ++i)
    {
        std::string arg(argv[i]);
        if (arg == "--mesh" && i + 1 < argc)
        {
            _string_params["mesh"] = argv[++i];
        }
        else if (arg == "--config" && i + 1 < argc)
        {
            _string_params["config"] = argv[++i];
        }
        else
        {
            // 忽略未知参数
        }
    }
    return true;
}

/*
 * 函数: set_string
 * 作用: 设置一个字符串参数（通常由 CLI 或测试注入）。
 */
void
ParameterReader::set_string(const std::string & key, const std::string & value)
{
    _string_params[key] = value;
}

/*
 * 函数: get_string
 * 作用: 获取一个字符串参数，若不存在返回 std::nullopt。
 */
std::optional<std::string>
ParameterReader::get_string(const std::string & key) const
{
    auto it = _string_params.find(key);
    if (it != _string_params.end())
        return it->second;
    return std::nullopt;
}

// 函数: get_bool
// 作用: 获取布尔参数，若不存在返回 nullopt
std::optional<bool>
ParameterReader::get_bool(const std::string & key) const
{
    auto it = _string_params.find(key);
    if (it != _string_params.end())
    {
        const std::string & val = it->second;
        if (iequals(val, "true") || val == "1")
            return true;
        if (iequals(val, "false") || val == "0")
            return false;
    }
    return std::nullopt;
}

// 函数: get_double
// 作用: 获取双精度浮点参数，若不存在返回 nullopt
std::optional<double>
ParameterReader::get_double(const std::string & key) const
{
    auto it = _string_params.find(key);
    if (it != _string_params.end())
    {
        try
        {
            return std::stod(it->second);
        }
        catch (const std::exception &)
        {
            // 转换失败，返回 nullopt
        }
    }
    return std::nullopt;
}

bool
ParameterReader::load_from_file(const std::string & path)
{
    std::ifstream ifs(path);
    if (!ifs.is_open())
    {
        return false;
    }
    std::stringstream buffer;
    buffer << ifs.rdbuf();
    const std::string json_text = buffer.str();
    // 复用基于字符串的加载入口，避免在此处重复维护解析细节
    return load_from_string(json_text);
}

// 函数: load_from_string
// 作用: 从 JSON 文本载入参数，使用不可变配置目录进行解析。
bool
ParameterReader::load_from_string(const std::string & json_text)
{
    DiagnosticList diags; // 可选诊断（当前不向外抛出）
    ConfigDirectory root = build_config_directory_from_json_text(json_text, &diags);

    // 清空旧状态
    _area_epsl.clear();
    _area_cond.clear();
    _area_mu.clear();

    _aniso_conductivity.clear();
    _aniso_magnetic.clear();
    _aniso_dielectric.clear();

    _frequencies.clear();
    _layers.clear();
    _boundary_specs.clear();
    _collect_point_spec.reset();

    // 标量参数
    if (auto tol = root.get_double("SOLVER_TOL"))
        _solver_tolerance = *tol;
    else
        _solver_tolerance.reset();

    if (auto iters = root.get_int64("SOLVER_MAX_ITERS"))
        _solver_max_iterations = static_cast<int>(*iters);
    else
        _solver_max_iterations.reset();

    // 读取分组数量 N_GROUP（可选）
    if (auto ng = root.get_int64("N_GROUP"))
        _num_groups = static_cast<int>(*ng);
    else
        _num_groups.reset();

    // PHYSICAL_AREA: EPSL/COND/MU
    parse_id_value_array_from_config(root, "EPSL", _area_epsl);
    parse_id_value_array_from_config(root, "COND", _area_cond);
    parse_id_value_array_from_config(root, "MU", _area_mu);

    // FREQUENCY: 支持纯数值或对象 { value: ... }
    if (auto freq_arr = root.get_array("FREQUENCY"))
    {
        for (const auto & v : freq_arr->items)
        {
            if (!v)
                continue;
            if (std::holds_alternative<double>(v->value))
            {
                _frequencies.push_back(std::get<double>(v->value));
            }
            else if (std::holds_alternative<int64_t>(v->value))
            {
                _frequencies.push_back(static_cast<double>(std::get<int64_t>(v->value)));
            }
            else if (std::holds_alternative<std::shared_ptr<ConfigObject>>(v->value))
            {
                auto obj_ptr = std::get<std::shared_ptr<ConfigObject>>(v->value);
                if (!obj_ptr)
                    continue;
                ConfigDirectory item_dir("freq", *obj_ptr, {});
                if (auto val = item_dir.get_double("value"))
                    _frequencies.push_back(*val);
            }
        }
    }

    // 各向异性数组解析通用模板
    auto parse_aniso = [&](const std::string & key,
                           std::unordered_map<int, AnisotropySpecEntry> & out,
                           const char * k1,
                           const char * k2,
                           const char * k3)
    {
        out.clear();
        if (auto arr = root.get_array(key))
        {
            for (const auto & item : arr->items)
            {
                if (!item)
                    continue;
                if (!std::holds_alternative<std::shared_ptr<ConfigObject>>(item->value))
                    continue;
                auto obj_ptr = std::get<std::shared_ptr<ConfigObject>>(item->value);
                if (!obj_ptr)
                    continue;
                ConfigDirectory it(key, *obj_ptr, {});
                auto area = it.get_int64("area");
                auto v1 = it.get_double(k1);
                auto v2 = it.get_double(k2);
                auto v3 = it.get_double(k3);
                double strike = it.get_double("strike").value_or(0.0);
                double dip = it.get_double("dip").value_or(0.0);
                double slant = it.get_double("slant").value_or(0.0);
                if (area && v1 && v2 && v3)
                {
                    AnisotropySpecEntry e;
                    e.area_id = static_cast<int>(*area);
                    e.principal_1 = *v1;
                    e.principal_2 = *v2;
                    e.principal_3 = *v3;
                    e.strike_deg = strike;
                    e.dip_deg = dip;
                    e.slant_deg = slant;
                    out[e.area_id] = e;
                }
            }
        }
    };

    parse_aniso("Anisotropicc", _aniso_conductivity, "sigma1", "sigma2", "sigma3");
    parse_aniso("Anisotropicm", _aniso_magnetic, "magnetic1", "magnetic2", "magnetic3");
    parse_aniso("Anisotropicd", _aniso_dielectric, "dielectric1", "dielectric2", "dielectric3");

    // 层化模型 LAYER_DEPTH_CONDUCTION
    if (auto arr = root.get_array("LAYER_DEPTH_CONDUCTION"))
    {
        for (const auto & item : arr->items)
        {
            if (!item)
                continue;
            if (!std::holds_alternative<std::shared_ptr<ConfigObject>>(item->value))
                continue;
            auto obj_ptr = std::get<std::shared_ptr<ConfigObject>>(item->value);
            if (!obj_ptr)
                continue;
            ConfigDirectory it("layer", *obj_ptr, {});
            auto id = it.get_int64("id");
            auto depth = it.get_double("depth");
            auto cond = it.get_double("cond");
            if (id && depth && cond)
            {
                LayerDepthConduction layer;
                layer.id = static_cast<int>(*id);
                layer.depth = *depth;
                layer.conductivity = *cond;
                _layers.push_back(layer);
            }
        }
    }

    // BOUNDARY_CONDITIONS
    if (auto bcs = root.get_array("BOUNDARY_CONDITIONS"))
    {
        for (const auto & bc : bcs->items)
        {
            if (!bc)
                continue;
            if (!std::holds_alternative<std::shared_ptr<ConfigObject>>(bc->value))
                continue;
            auto obj_ptr = std::get<std::shared_ptr<ConfigObject>>(bc->value);
            if (!obj_ptr)
                continue;
            ConfigDirectory it("bc", *obj_ptr, {});

            auto inline_name_opt = it.get_string("name");

            // 解析类型
            auto type_str = it.get_string("type");
            if (!type_str)
                continue;
            auto type_opt = parse_boundary_type_from_string(*type_str);
            if (!type_opt.has_value())
                continue;

            // 标量/alpha 值
            double value = 0.0;
            if (auto v = it.get_double("value"))
                value = *v;
            if (auto v = it.get_double("alpha"))
                value = *v; // 对于 Robin/Impedance 记到 value

            // 可选方向: direction: [dx, dy, dz]
            bool has_dir = false;
            std::array<double, 3> dir{{1.0, 0.0, 0.0}};
            if (auto darr = it.get_array("direction"))
            {
                std::array<double, 3> tmp{{0.0, 0.0, 0.0}};
                size_t count = 0;
                for (const auto & dv : darr->items)
                {
                    if (!dv)
                        continue;
                    double val = 0.0;
                    if (std::holds_alternative<int64_t>(dv->value))
                        val = static_cast<double>(std::get<int64_t>(dv->value));
                    else if (std::holds_alternative<double>(dv->value))
                        val = std::get<double>(dv->value);
                    if (count < 3)
                        tmp[count++] = val;
                }
                if (count == 3)
                {
                    dir = tmp;
                    has_dir = true;
                }
            }

            // 复数/背景模式
            BoundaryComplexMode mode = BoundaryComplexMode::RealValue;
            if (auto m = it.get_string("mode"))
            {
                std::string ms = *m;
                std::transform(ms.begin(), ms.end(), ms.begin(), [](unsigned char c) {
                    return std::tolower(c);
                });
                if (ms == "real" || ms == "realvalue")
                    mode = BoundaryComplexMode::RealValue;
                else if (ms == "complex" || ms == "complexvalue")
                    mode = BoundaryComplexMode::ComplexValue;
                else if (ms == "layered1d_electric" || ms == "layered1delectric")
                    mode = BoundaryComplexMode::Layered1DElectric;
                else if (ms == "layered1d_magnetic" || ms == "layered1dmagnetic")
                    mode = BoundaryComplexMode::Layered1DMagnetic;
            }
            double value_real = it.get_double("value_real").value_or(0.0);
            double value_imag = it.get_double("value_imag").value_or(0.0);

            // 优先从新格式解析 ids
            std::vector<int> boundary_ids;
            if (auto ids_arr = it.get_array("ids"))
            {
                for (const auto & idv : ids_arr->items)
                {
                    if (!idv)
                        continue;
                    if (std::holds_alternative<int64_t>(idv->value))
                        boundary_ids.push_back(static_cast<int>(std::get<int64_t>(idv->value)));
                    else if (std::holds_alternative<double>(idv->value))
                        boundary_ids.push_back(static_cast<int>(std::get<double>(idv->value)));
                }
            }

            if (boundary_ids.empty())
                continue; // 无可用的边界 id

            for (int bid : boundary_ids)
            {
                BoundarySpec spec;
                spec.boundary_id = bid;
                spec.type = *type_opt;
                spec.value = value;
                spec.direction = dir;
                spec.has_direction = has_dir;
                spec.mode = mode;
                spec.value_real = value_real;
                spec.value_imag = value_imag;
                _boundary_specs.push_back(spec);
            }
        }
    }

    // COLLECT_POINT: about/start/step/number 三轴规格
    if (auto cparr = root.get_array("COLLECT_POINT"))
    {
        CollectPointSpec cps;
        for (const auto & v : cparr->items)
        {
            if (!v)
                continue;
            if (!std::holds_alternative<std::shared_ptr<ConfigObject>>(v->value))
                continue;
            auto obj_ptr = std::get<std::shared_ptr<ConfigObject>>(v->value);
            if (!obj_ptr)
                continue;
            ConfigDirectory it("collect", *obj_ptr, {});
            auto about_opt = it.get_string("about");
            if (!about_opt)
                continue;
            std::string about = *about_opt;
            if (!about.empty())
                about[0] = static_cast<char>(std::tolower(static_cast<unsigned char>(about[0])));

            double start = it.get_double("start").value_or(0.0);
            double step = it.get_double("step").value_or(0.0);
            int number = static_cast<int>(it.get_int64("number").value_or(0));

            if (about == "x")
            {
                cps.has_x = true;
                cps.x.about = 'x';
                cps.x.start = start;
                cps.x.step = step;
                cps.x.number = number;
            }
            else if (about == "y")
            {
                cps.has_y = true;
                cps.y.about = 'y';
                cps.y.start = start;
                cps.y.step = step;
                cps.y.number = number;
            }
            else if (about == "z")
            {
                cps.has_z = true;
                cps.z.about = 'z';
                cps.z.start = start;
                cps.z.step = step;
                cps.z.number = number;
            }
        }
        if (cps.has_x || cps.has_y || cps.has_z)
            _collect_point_spec = cps;
    }

    // 材料库占位（暂不从 JSON 构建）
    return true;
}

// 函数: compute_collect_point_values
// 作用: 基于 _collect_point_spec 计算三轴坐标数组。
CollectPointValues
ParameterReader::compute_collect_point_values() const
{
    CollectPointValues out;
    if (!_collect_point_spec.has_value())
        return out;
    auto gen = [](const CollectAxisSpec & s)
    {
        std::vector<double> v;
        if (s.number <= 0)
            return v;
        v.resize(static_cast<size_t>(s.number));
        for (int i = 0; i < s.number; ++i)
            v[static_cast<size_t>(i)] = s.start + static_cast<double>(i) * s.step;
        return v;
    };
    if (_collect_point_spec->has_x)
        out.x_coords = gen(_collect_point_spec->x);
    if (_collect_point_spec->has_y)
        out.y_coords = gen(_collect_point_spec->y);
    if (_collect_point_spec->has_z)
        out.z_coords = gen(_collect_point_spec->z);
    return out;
}

std::optional<BoundaryType>
ParameterReader::parse_boundary_type_from_string(const std::string & s) const
{
    if (iequals(s, "dirichlet"))
        return BoundaryType::Dirichlet;
    if (iequals(s, "neumann"))
        return BoundaryType::Neumann;
    if (iequals(s, "robin"))
        return BoundaryType::Robin;
    if (iequals(s, "impedance"))
        return BoundaryType::Impedance;
    return std::nullopt;
}

} // namespace cem